﻿namespace IBTrader.Frontend.Trader
{
    using IBTrader.Charts;
    using IBTrader.Charts.Drawing;
    using IBTrader.Charts.Legend;
    using IBTrader.Charts.Windows;
    using IBTrader.Indicator;
    using IBTrader.Indicator.CandleStick;
    using IBTrader.Model;
    using IBTrader.Modules;
    using IBTrader.Modules.Configuration;
    using IBTrader.Modules.Read;
    using IBTrader.Properties;
    using Microsoft.Practices.Prism.Commands;
    using MvvmValidation;
    using NLog;
    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Linq;
    using System.Windows;

    class ViewModel : ViewModelBase
    {
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();
        public event EventHandler<Modules.Market.Order.EndArgs> End;
        public DelegateCommand Start { get; private set; }
        public DelegateCommand Cancel { get; private set; }
        public DelegateCommand Reset { get; private set; }
        public DelegateCommand SizeChanged { get; private set; }
        public DelegateCommand IsVisibleChanged { get; private set; }
        public DelegateCommand PreviousSymbol { get; private set; }
        public DelegateCommand NextSymbol { get; private set; }
        public DelegateCommand PreviousDate { get; private set; }
        public DelegateCommand NextDate { get; private set; }
        private Algo algo;
        private Worker worker;
        private ReaderSwitch readerSwitch;
        private bool isExecuting;
        private IIndicator indicator;

        public ViewModel() : base(ComponentsSection.Components.Trader)
        {            
            if ((bool)DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, typeof(DependencyObject)).Metadata.DefaultValue) return;

            worker = Creator.Select<Worker>(objects);
            indicator = Creator.Select<IIndicator>(objects);
            readerSwitch = Creator.Select<ReaderSwitch>(objects);

            Func<bool> canRead = () => readerSwitch != null && readerSwitch.Reader != null;
            Func<bool> canExecute = () => canRead() && !isExecuting;
            Start = new DelegateCommand(DoStart, () => canExecute());
            Cancel = new DelegateCommand(DoEnd, () => isExecuting);
            Reset = new DelegateCommand(DoReset, () => canExecute());
            SizeChanged = new DelegateCommand(() => Redraw(this, EventArgs.Empty));
            IsVisibleChanged = new DelegateCommand(() => Redraw(this, EventArgs.Empty));
            PreviousSymbol = new DelegateCommand(() => Change(Algo.BacklogConfiguration.ChangeSymbol, -1), () => canExecute());
            NextSymbol = new DelegateCommand(() => Change(Algo.BacklogConfiguration.ChangeSymbol, +1), () => canExecute());
            PreviousDate = new DelegateCommand(() => Change(Algo.BacklogConfiguration.ChangeDate, -1), () => canExecute());
            NextDate = new DelegateCommand(() => Change(Algo.BacklogConfiguration.ChangeDate, +1), () => canExecute());

            Func<bool> isLive = () => Source == typeof(ReaderRealTime).ToString();
            Validator.AddRequiredRule(() => Algo.Contract.Symbol, "You need to enter a Stock Symbol");
            Validator.AddRule(() => RuleResult.Assert(isLive() || !string.IsNullOrWhiteSpace(Algo.BacklogConfiguration.Folder), "Folder must not be empty."));
            Validator.AddRule(() => RuleResult.Assert(isLive() || Algo.BacklogConfiguration.TextFile().Exists, 
                string.Format("backlog data file [{0}] not found. Consider using 'Collect Daily Tick Data' and 'Collect Historical Data' "+
                "to collect backlog data first.",Algo.BacklogConfiguration.TextFile().FullName)));
            Validator.AddRule(() => RuleResult.Assert(Algo.IndicatorInterval > 0, "Indicator interval must be greater then zero"));

            if (AutoConnect) Source = typeof(ReaderRealTime).FullName;
        }
        protected override void OnLoad(System.Windows.Controls.Control view)
        {
            if (IsLoaded) return;
            if (IB == null) return;

            base.OnLoad(view);

            Algo = Store.Algos.Single();
            Algo.Clear();
            Algo.BacklogConfiguration.PropertyChanged += (o, p) => { if (p.PropertyName == "FileFormat") Source = Source; }; // TODO:

            worker.Reader.Last += new LegendPrice(worker).Add;
            worker.Reader.Last += new LegendTime(worker).Add;
            worker.Reader.Volume += new LegendText(worker, Algo.Contract.Symbol).Add;
            if (indicator is IVolume)
                worker.Reader.Volume += (indicator as IVolume).AddVolume;
            if (indicator is CandleStick || indicator is CandleStickPattern || indicator is HeikinAshi)
                indicator.Hit += new ChartCandleStick(worker) { Color = Color.Red }.Add;
            if (indicator is PivotPoints)
                (indicator as PivotPoints).Hit += new ChartLines(worker, Color.Violet).Add;
            if (indicator is IUpDown)
            {
                (indicator as IUpDown).Up += new ChartLine(worker, Color.Red).Add;
                (indicator as IUpDown).Down += new ChartLine(worker, Color.LightGreen).Add;
            }
            //indicator.Hit += new ChartLine(worker, Color.Orange).Add;
        }
        private void DoStart()
        {
            W.Q(logger, () =>
            {
                if (!IsValid()) return;
                UpdateCommands(true);
                DoReset();
                W.X(logger, () => worker.Reader.Do(Algo.BacklogConfiguration));
                Redraw(this, EventArgs.Empty);
                UpdateCommands(false);
                Algo.Orders.UpdateSummary();
            });
        }
        private void UpdateCommands(bool isExecuting)
        {
            this.isExecuting = isExecuting;
            Update(Start, Cancel, Reset, PreviousSymbol, NextSymbol, PreviousDate, NextDate);
        }
        private void DoReset()
        {
            Invoke(Algo.Clear);
            worker.Filters.OfType<BaseChart>().ToList().ForEach(c => c.Clear());
            worker.Filters.OfType<BasePrices>().ToList().ForEach(c => c.Prices.Clear());
            Redraw(this, EventArgs.Empty);
        }
        private void DoEnd()
        {
            if (End != null)
                End(this, Modules.Market.Order.EndArgs.Empty);
        }
        public void Redraw(object _, EventArgs __)
        {
            Invoke(() =>
            {
                var control = base.view as Control;
                var charts = worker.Filters.OfType<IChart>();

                control.Image.Source = new ImageSourceGraphics(charts, Math.Max(1, control.Image.ActualWidth), Math.Max(1, control.Image.ActualHeight)).BitmapImage;
                //new CanvasGraphics(control.Canvas).Draw(charts, control.CanvasGrid.ActualWidth, control.CanvasGrid.ActualHeight);
            });
        }
        private void Change(Func<int, bool> change, int offset)
        {
            change(offset);
            DoStart();
        }
        public override bool AutoConnect
        {
            get { return Settings.Default.TraderAutoConnect; }
            set
            {
                Settings.Default.TraderAutoConnect = value;
                Settings.Default.Save();
                Changed(() => AutoConnect);
            }
        }
        public Algo Algo
        {
            get { return algo; }
            set { algo = value; Changed(() => Algo); }
        }
        public string Source
        {
            get
            {
                if (readerSwitch != null && readerSwitch.Reader != null )
                    return readerSwitch.Reader.GetType() == typeof(ReaderRealTime) ? readerSwitch.Reader.GetType().ToString() : "Backlog";
                return null;
            }
            set 
            {
                if (value != null && readerSwitch != null)
                {
                    readerSwitch.Switch(value == "Backlog" 
                        ? Algo.BacklogConfiguration.FileFormats.Single(f => f.Value == Algo.BacklogConfiguration.FileFormat).Type 
                        : Type.GetType(value));
                    //Changed(() => Source);
                    UpdateCommands(isExecuting);
                }
            }
        }        
    }
}
